package com.codeoss.framework.webide.service.impl;

import java.io.ByteArrayOutputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.codeoss.framework.codegenerator.CodeGeneratorUtil;
import com.codeoss.framework.context.YunContext;
import com.codeoss.framework.data.metadata.Column;
import com.codeoss.framework.data.metadata.Query;
import com.codeoss.framework.data.metadata.Restrictions;
import com.codeoss.framework.data.metadata.Table;
import com.codeoss.framework.data.parser.ITableParser;
import com.codeoss.framework.data.store.ITableStore;
import com.codeoss.framework.db.service.IDDLService;
import com.codeoss.framework.db.service.impl.IDDLServiceStore;
import com.codeoss.framework.db.util.ValueNamePairUtil;
import com.codeoss.framework.impexp.ExportManager;
import com.codeoss.framework.util.DBUtil;
import com.codeoss.framework.util.StringUtils;
import com.codeoss.framework.util.TableBuilder;
import com.codeoss.framework.util.ValueNamePair;
import com.codeoss.framework.webide.request.ModelFormPageRequest;
import com.codeoss.framework.webide.response.ModelFormPageResponse;
import com.codeoss.framework.webide.service.IModelFormPageService;
import com.codeoss.module.ir.model.IrModel;
import com.codeoss.module.ir.model.IrModelAccess;
import com.codeoss.module.ir.service.IIrModelAccessService;
import com.codeoss.module.ir.service.IIrModelService;
import com.fasterxml.jackson.databind.ObjectMapper;

@Service
public class ModelFormPageService implements IModelFormPageService {
	@Autowired
	private IIrModelService irModelService;
	@Autowired
	private IIrModelAccessService irModelAccessService;
	@Autowired
	private ITableStore tableStore;
	@Autowired
	private ExportManager exportManager;
	@Value("${framework.db-type}")
	private String dbType;
	@Autowired
	private DBUtil dbUtil;
	@Autowired
	private ITableParser tableParser;
	@Autowired
	private ObjectMapper objectMapper;
	@Autowired
	private DataSource dataSource;
	@Autowired
	private IDDLServiceStore ddlServiceStore;
	@Value("${framework.base-path}")
	private String basePath;
	@Autowired
	private ValueNamePairUtil valueNamePairUtil;
	@Override
	public ModelFormPageResponse init(String tableName) {
		// TODO Auto-generated method stub
		ModelFormPageResponse modelFormPageResponse = new ModelFormPageResponse();
		YunContext yunContext=YunContext.getCurrentContext();
		Query modelQuery=new Query();
		modelQuery.add(Restrictions.eq("model_type", "table"));
		Table modelTable=tableStore.get("ir_model");
		List<ValueNamePair> tableList = valueNamePairUtil.getValueNamePairList(yunContext, modelTable, modelQuery, false, true);
		modelFormPageResponse.setTableList(tableList);
	
		Table dictTable=tableStore.get("ir_dict");
		List<ValueNamePair> irDictPairList =valueNamePairUtil.getValueNamePairList(yunContext, dictTable, null, false, true);
		modelFormPageResponse.setDictList(irDictPairList);
		IrModel irModel = irModelService.getIrModelByTableName(tableName);

		modelFormPageResponse.setModelType(irModel.getModelType());
		
		QueryWrapper<IrModelAccess> modelAccessQuery = new QueryWrapper<IrModelAccess>();
		modelAccessQuery.eq("ir_model_id", irModel.getId());
		List<IrModelAccess> modelAccessList = irModelAccessService.list(modelAccessQuery);
		List<Map<String, Object>> modelAccessMapList = new ArrayList<Map<String, Object>>();
		modelFormPageResponse.setModelAccessList(modelAccessMapList);
		for (int i = 0; i < modelAccessList.size(); i++) {
			IrModelAccess irModelAccess = modelAccessList.get(i);
			Map<String, Object> map = new HashMap<String, Object>();
			modelAccessMapList.add(map);
			map.put("id", irModelAccess.getId());
			map.put("resRoleId", irModelAccess.getResRoleId());
			map.put("permSelect", irModelAccess.getPermSelect());
			map.put("permInsert", irModelAccess.getPermInsert());
			map.put("permUpdate", irModelAccess.getPermUpdate());
			map.put("permDelete", irModelAccess.getPermDelete());
		}

		Table roleTable=tableStore.get("res_role");
		List<ValueNamePair> rolePairList =valueNamePairUtil.getValueNamePairList(yunContext, roleTable, null, true, false);
		modelFormPageResponse.setRoleList(rolePairList);

		Map<String, Object> tableMap = new HashMap<String, Object>();
		modelFormPageResponse.setTableMap(tableMap);
		tableMap.put("tableName", irModel.getTableName());
		tableMap.put("name", irModel.getName());
		tableMap.put("jsonContent", irModel.getJsonContent());
		return modelFormPageResponse;
	}

	@Override
	@Transactional
	public ModelFormPageResponse dataSave(ModelFormPageRequest request) {
		// TODO Auto-generated method stub
		ModelFormPageResponse modelFormPageResponse = new ModelFormPageResponse();
		QueryWrapper<IrModel> irModelQuery = new QueryWrapper<IrModel>();
		irModelQuery.eq("table_name", request.getTableName());
		IrModel irModel = irModelService.getOne(irModelQuery);
		if (irModel == null) {
			irModel = new IrModel();
			irModel.setTableName(request.getTableName());
			irModel.setName(request.getName());
		} else {
			irModel.setName(request.getName());
		}
		irModel.setJsonContent(request.getJsonContent());
		if (irModel.getId() == null) {
			irModelService.insert(irModel);
		} else {
			irModelService.update(irModel);
		}
		tableStore.remove(request.getTableName());
		return modelFormPageResponse;
	}

	@Override
	public ModelFormPageResponse dataExport(String tableName) {
		// TODO Auto-generated method stub
		ModelFormPageResponse modelFormPageResponse = new ModelFormPageResponse();
		YunContext yunContext = YunContext.getCurrentContext();
		Table table = tableStore.get("ir_model");
		Query query = new Query();
		query.add(Restrictions.eq("table_name", tableName));
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		try {
			exportManager.exportProperties(yunContext, table, query, os);
			String fileContent = new String(os.toByteArray(), "utf-8");
			String fileName = "model_" + tableName + ".prop";
			modelFormPageResponse.setFileContent(fileContent);
			modelFormPageResponse.setFileName(fileName);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new RuntimeException(e);
		}
		return modelFormPageResponse;
	}

	@Override
	@Transactional
	public ModelFormPageResponse syncTable(String tableName, boolean singleTable) {
		// TODO Auto-generated method stub
		ModelFormPageResponse modelFormPageResponse = new ModelFormPageResponse();
		Table table = TableBuilder.build(tableName, objectMapper, tableParser, irModelService);
		if (!table.isAuto()) {
			return modelFormPageResponse;
		}
		List<String> dbColumnList = getColumnList(tableName);
		Connection connection = DataSourceUtils.getConnection(dataSource);
		try {
			IDDLService ddlService = ddlServiceStore.getDDLService();
			if(table.isView()) {
				ddlService.createView(connection, table);
				return modelFormPageResponse;
			}
			if (dbColumnList.size() == 0) {
				ddlService.createTable(connection, table);
				if (singleTable) {
					List<Column> columnList = table.getAllColumnList();
					for (int i = 0; i < columnList.size(); i++) {
						Column column = (Column) columnList.get(i);
						if (column.isUnique()) {
							ddlService.createUniqueIndex(connection, table, column);
						} else if (column.getLookupInfo() != null
								&& column.getLookupInfo().getDataSourceConfig() != null) {
							ddlService.createForeignKey(connection, table, column);
						}
					}
				} else {
					YunContext yunContext = YunContext.getCurrentContext();
					Map<Table, List<Column>> columnMap = yunContext.getAttribute("__columnMap__");
					if (columnMap == null) {
						columnMap = new HashMap<Table, List<Column>>();
						yunContext.setAttribute("__columnMap__", columnMap);
					}
					columnMap.put(table, table.getAllColumnList());
				}
			} else {
				List<Column> allColumnList = table.getAllColumnList();
				List<Column> createColumnList = new ArrayList<Column>();
				for (int i = 0; i < allColumnList.size(); i++) {
					Column dataColumn = allColumnList.get(i);
					String columnName = dataColumn.getColumnName();
					if (!dbColumnList.contains(columnName)) {
						createColumnList.add(dataColumn);
					}
				}
				for (int i = 0; i < createColumnList.size(); i++) {
					Column column = (Column) createColumnList.get(i);
					ddlService.createColumn(connection, table, column);
				}
				if (singleTable) {
					for (int i = 0; i < createColumnList.size(); i++) {
						Column column = (Column) createColumnList.get(i);
						if (column.isUnique()) {
							ddlService.createUniqueIndex(connection, table, column);
						} else if (column.getLookupInfo() != null
								&& column.getLookupInfo().getDataSourceConfig() != null) {
							ddlService.createForeignKey(connection, table, column);
						}
					}
				} else {
					YunContext yunContext = YunContext.getCurrentContext();
					Map<Table, List<Column>> columnMap = yunContext.getAttribute("__columnMap__");
					if (columnMap == null) {
						columnMap = new HashMap<Table, List<Column>>();
						yunContext.setAttribute("__columnMap__", columnMap);
					}
					columnMap.put(table, createColumnList);
				}
			}
			if (singleTable) {
				addMissingIndex(tableName);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			DataSourceUtils.releaseConnection(connection, dataSource);
		}
		return modelFormPageResponse;
	}

	private void addMissingIndex(String tableName) {
		IDDLService ddlService = ddlServiceStore.getDDLService();
		Connection connection = DataSourceUtils.getConnection(dataSource);
		try {
			DatabaseMetaData dbMetaData = connection.getMetaData();
			Map<String, Object> indexMap = new HashMap<String, Object>();
			String tableName2 = tableName;
			if (dbType.equals("oracle")) {
				tableName2 = tableName.toUpperCase();
			}
			ResultSet rs = dbMetaData.getIndexInfo(null, null, tableName2, false, true);
			while (rs.next()) {
				String tName = rs.getString("TABLE_NAME");
				String cName = rs.getString("COLUMN_NAME");
				if (tName != null) {
					tName = tName.toLowerCase();
				}
				if (cName != null) {
					cName = cName.toLowerCase();
				}
				indexMap.put(tName + "::" + cName, "");
			}
			dbUtil.closeResultSet(rs);
			rs = dbMetaData.getImportedKeys(null, null, tableName2);
			while (rs.next()) {
				String tName = rs.getString("FKTABLE_NAME");
				String cName = rs.getString("FKCOLUMN_NAME");
				if (tName != null) {
					tName = tName.toLowerCase();
				}
				if (cName != null) {
					cName = cName.toLowerCase();
				}
				indexMap.put(tName + "::" + cName, "");
			}
			dbUtil.closeResultSet(rs);
			Table table = tableStore.get(tableName);
			List<Column> columnList = table.getAllColumnList();
			for (int j = 0; j < columnList.size(); j++) {
				Column column = columnList.get(j);
				if (column.isUnique()) {
					if (indexMap.get(tableName + "::" + column.getColumnName()) != null) {
						continue;
					}
					ddlService.createUniqueIndex(connection, table, column);
				} else if (column.getLookupInfo() != null && column.getLookupInfo().getDataSourceConfig() != null) {
					if (indexMap.get(tableName + "::" + column.getColumnName()) != null) {
						continue;
					}
					ddlService.createForeignKey(connection, table, column);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			DataSourceUtils.releaseConnection(connection, dataSource);
		}
	}

	private List<String> getColumnList(String tableName) {
		Connection connection = DataSourceUtils.getConnection(dataSource);
		ResultSet rs = null;
		List<String> columnList = new ArrayList<String>();
		try {
			DatabaseMetaData md = connection.getMetaData();
			String tableName2 = tableName;
			if (dbType.equals("oracle")) {
				tableName2 = tableName.toUpperCase();
			}
			rs = md.getColumns(null, null, tableName2, null);
			while (rs.next()) {
				String columnName = rs.getString("COLUMN_NAME");
				if (dbType.equals("oracle")) {
					columnName = columnName.toLowerCase();
				}
				columnList.add(columnName);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException(e);
		} finally {
			dbUtil.closeResultSet(rs);
			DataSourceUtils.releaseConnection(connection, dataSource);
		}
		return columnList;
	}

	@Override
	@Transactional
	public ModelFormPageResponse modelAccessSave(ModelFormPageRequest request) {
		// TODO Auto-generated method stub
		ModelFormPageResponse modelFormPageResponse=new ModelFormPageResponse();
		IrModel irModel = irModelService.getIrModelByTableName(request.getTableName());

		QueryWrapper<IrModelAccess> modelAccessQuery = new QueryWrapper<IrModelAccess>();
		modelAccessQuery.eq("ir_model_id", irModel.getId());
		List<IrModelAccess> modelAccessList = irModelAccessService.list(modelAccessQuery);
		
		List<Map<String,String>> modelAccessMapList=request.getModelAccessList();
		for(int i=0;i<modelAccessMapList.size();i++) {
			Map<String,String> map=modelAccessMapList.get(i);
			String sId=map.get("id");
			IrModelAccess irModelAccess=null;
			if(StringUtils.isEmpty(sId)) {
				irModelAccess=new IrModelAccess();
				irModelAccess.setIrModelId(irModel.getId());
			}else {
				irModelAccess=irModelAccessService.getById(Long.parseLong(sId));
				for(int j=0;j<modelAccessList.size();j++) {
					if(modelAccessList.get(j).getId().equals(irModelAccess.getId())) {
						modelAccessList.remove(j);
						break;
					}
				}
			}
			Long resRoleId=Long.parseLong(map.get("resRoleId"));
			irModelAccess.setResRoleId(resRoleId);
			
			String sPermSelect=map.get("permSelect");
			String sPermUpdate=map.get("permUpdate");
			String sPermDelete=map.get("permDelete");
			String sPermInsert=map.get("permInsert");
			if(StringUtils.isEmpty(sPermSelect)) {
				irModelAccess.setPermSelect(false);
			}else if("true".equals(sPermSelect)) {
				irModelAccess.setPermSelect(true);
			}else if("false".equals(sPermSelect)) {
				irModelAccess.setPermSelect(false);
			}
			if(StringUtils.isEmpty(sPermUpdate)) {
				irModelAccess.setPermUpdate(false);
			}else if("true".equals(sPermUpdate)) {
				irModelAccess.setPermUpdate(true);
			}else if("false".equals(sPermUpdate)) {
				irModelAccess.setPermUpdate(false);
			}
			if(StringUtils.isEmpty(sPermDelete)) {
				irModelAccess.setPermDelete(false);
			}else if("true".equals(sPermDelete)) {
				irModelAccess.setPermDelete(true);
			}else if("false".equals(sPermDelete)) {
				irModelAccess.setPermDelete(false);
			}
			if(StringUtils.isEmpty(sPermInsert)) {
				irModelAccess.setPermInsert(false);
			}else if("true".equals(sPermInsert)) {
				irModelAccess.setPermInsert(true);
			}else if("false".equals(sPermInsert)) {
				irModelAccess.setPermInsert(false);
			}
			if(irModelAccess.getId()==null) {
				irModelAccessService.insert(irModelAccess);
			}else {
				irModelAccessService.update(irModelAccess);
			}
		}
		
		for(int i=0;i<modelAccessList.size();i++) {
			irModelAccessService.delete(modelAccessList.get(i));
		}
		return modelFormPageResponse;
	}

	@Override
	public ModelFormPageResponse generateCode(ModelFormPageRequest request) {
		// TODO Auto-generated method stub
		ModelFormPageResponse modelFormPageResponse=new ModelFormPageResponse();
		QueryWrapper<IrModel> modelQuery=new QueryWrapper<IrModel>();
		modelQuery.in("model_type","table","view");
		List<IrModel> modelList=irModelService.list(modelQuery);
		List<Table> tableList=new ArrayList<Table>();
		for(int i=0;i<modelList.size();i++) {
			IrModel irModel=modelList.get(i);
			Table table = TableBuilder.build(irModel.getTableName(), objectMapper, tableParser, irModelService);
			if(table.isAuto()) {
				tableList.add(table);
			}
		}
		//CodeGeneratorUtil.generateAllModel(tableList, basePath);
		
		
		Table table = TableBuilder.build(request.getTableName(), objectMapper, tableParser, irModelService);
		String folderName = CodeGeneratorUtil.generate(basePath, table, request.isGenerateModel(),request.isGenerateDAO(),request.isGenerateService());
		modelFormPageResponse.setFolderName(folderName);
		return modelFormPageResponse;
	}
}
